{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "scrolled": false
   },
   "source": [
    "# Examples of Hamiltonian Conversion\n",
    "This page contains several examples of converting the hamiltonian of specific molecules to the Jordan-Wigner form and the Bravyi-Kitaev form"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Some Background & Terminologies\n",
    "### Hamiltonian in the Second Quantization Form\n",
    "In order to prepare the hamiltonian for VQE, we will first express the molecular hamiltonian in the second quantization form:\n",
    "$$\n",
    "\\begin{align}\n",
    "H = \\sum_{ij}h_{ij}a^\\dagger_{i}a_{j} + \\sum_{ijkl}h_{ijkl}a^\\dagger_{i}a^\\dagger_{j}a_{k}a_{l}\n",
    "\\end{align}\n",
    "$$\n",
    "The first summation is the one-electron integral, which includes the kinetic potential and the coulumb interaction (electron-nuclear). Its coefficient $h_{ij}$ can be expressed as:\n",
    "$$\n",
    "\\begin{align}\n",
    "h_{ij} = \\int<\\psi_{i}(x)|-\\frac{1}{2}\\nabla^{2}-\\sum_{\\alpha}\\frac{Z_{\\alpha}}{r_{\\alpha,x}}|\\psi_{j}(x)>dx\n",
    "\\end{align}\n",
    "$$\n",
    "The second summation is the two-electron integral, which includes all the electron-electron interaction. Its coefficient $h_{ijkl}$ can be expressed as:\n",
    "$$\n",
    "\\begin{align}\n",
    "h_{ijkl} = \\int<\\psi_{i}(x_{1})|<\\psi_{j}(x_{2})|\\psi_{k}(x_{2})>|\\psi_{l}(x_{1})>dx_{1}dx_{2}\n",
    "\\end{align}\n",
    "$$\n",
    "Calculating the above coefficients numerically or analytically can be very costly. We will use OpenFermion to obtain them for simplicity.\n",
    "\n",
    "### Fermionic Operator\n",
    "OpenFermion express the creation operator (i.e. $a^\\dagger_{i}$) and the annihilation operator (i.e. $a_{i}$) using the FermionOperator data structure. The following are examples of valid FermionOperators:\n",
    "$$\n",
    "\\begin{align}\n",
    "& a_1 \\nonumber \\\\\n",
    "& 1.7 a^\\dagger_3 \\nonumber \\\\\n",
    "&-1.7 \\, a^\\dagger_3 a_1 \\nonumber \\\\\n",
    "&(1 + 2i) \\, a^\\dagger_4 a^\\dagger_3 a_9 a_1 \\nonumber \\\\\n",
    "&(1 + 2i) \\, a^\\dagger_4 a^\\dagger_3 a_9 a_1 - 1.7 \\, a^\\dagger_3 a_1 \\nonumber\n",
    "\\end{align}\n",
    "$$\n",
    "\n",
    "Each fermionic operator is represented in a 2-tuple structure as:\n",
    "$$\n",
    "\\begin{align}\n",
    "I & \\mapsto () \\nonumber \\\\\n",
    "a_1 & \\mapsto ((1, 0),) \\nonumber \\\\\n",
    "a^\\dagger_3 & \\mapsto ((3, 1),) \\nonumber \\\\\n",
    "a^\\dagger_3 a_1 & \\mapsto ((3, 1), (1, 0)) \\nonumber \\\\\n",
    "a^\\dagger_4 a^\\dagger_3 a_9 a_1 & \\mapsto ((4, 1), (3, 1), (9, 0), (1, 0)) \\nonumber\n",
    "\\end{align}\n",
    "$$\n",
    "\n",
    "### Jordan-Wigner and Bravyi-Kitaev\n",
    "After obtaining the total hamiltonian, the next step is to convert it to some form that can be understood by the quantum computer. Electron are fermions with half integer spin and follows Fermi-Dirac distribution. On the other hand, qubit hamiltonian are bosonic with integer spin and follows Bose-Einstein distribution. OpenFermion currently allows two methods to transform the original fermionic hamiltonian to bosonic hamiltonian. The are called the Jordan-Wigner tranformation and Bravyi-Kitaev transformation. We will investigate more into this in the future, with the aim of obtaining more efficient mapping results. MORE INFORMATION NEEDED HERE"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Define the function to obtain the transformation\n",
    "This is the same for different molecules. You must run this block before running the examples below"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "import openfermion\n",
    "from openfermion.hamiltonians import MolecularData\n",
    "from openfermion.ops import FermionOperator\n",
    "from openfermion.ops import InteractionOperator\n",
    "from openfermion.transforms import get_fermion_operator, get_interaction_operator, jordan_wigner, bravyi_kitaev, bravyi_kitaev_fast\n",
    "\n",
    "def get_transformation(hamiltonian):\n",
    "    # get the fermionic hamiltonian (note: this is required for BK and BKSF)\n",
    "    fermionic_hamiltonian = get_fermion_operator(hamiltonian)\n",
    "    # print(\"the fermionic hamiltonian is:\")\n",
    "    # print(fermionic_hamiltonian)\n",
    "\n",
    "    # get the interaction hamiltonian (note: this is required for BKSF)\n",
    "    interaction_hamiltonian = get_interaction_operator(fermionic_hamiltonian)\n",
    "    # print(\"the interactive hamiltonian is:\")\n",
    "    # print(interactive_hamiltonian)\n",
    "    \n",
    "    # Get the JW form\n",
    "    jw_operator = jordan_wigner(hamiltonian)\n",
    "    print(\"the Jordan-Wigner form is:\")\n",
    "    print(jw_operator)\n",
    "    print()\n",
    "\n",
    "    # Get the BK form\n",
    "    bk_operator = bravyi_kitaev(fermionic_hamiltonian)\n",
    "    print(\"the Bravyi-Kitaev form is:\")\n",
    "    print(bk_operator)\n",
    "    print()\n",
    "\n",
    "    # Get the BKSF form\n",
    "    bksf_operator = bravyi_kitaev_fast(interaction_hamiltonian)\n",
    "    print(\"the Bravyi-Kitaev-Super-Fast form is:\")\n",
    "    print(bksf_operator)\n",
    "    print()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Example 1: Hydrogen Molecule ($H_{2}$)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "scrolled": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "the Jordan-Wigner form is:\n",
      "-0.09886397351781592 [] +\n",
      "-0.04532220209856541 [X0 X1 Y2 Y3] +\n",
      "0.04532220209856541 [X0 Y1 Y2 X3] +\n",
      "0.04532220209856541 [Y0 X1 X2 Y3] +\n",
      "-0.04532220209856541 [Y0 Y1 X2 X3] +\n",
      "0.17119774853325856 [Z0] +\n",
      "0.16862219143347554 [Z0 Z1] +\n",
      "0.12054482186554413 [Z0 Z2] +\n",
      "0.16586702396410954 [Z0 Z3] +\n",
      "0.17119774853325856 [Z1] +\n",
      "0.16586702396410954 [Z1 Z2] +\n",
      "0.12054482186554413 [Z1 Z3] +\n",
      "-0.22278592890107016 [Z2] +\n",
      "0.17434844170557132 [Z2 Z3] +\n",
      "-0.22278592890107016 [Z3]\n",
      "\n",
      "the Bravyi-Kitaev form is:\n",
      "(-0.09886397351781583+0j) [] +\n",
      "(0.04532220209856541+0j) [X0 Z1 X2] +\n",
      "(0.04532220209856541+0j) [X0 Z1 X2 Z3] +\n",
      "(0.04532220209856541+0j) [Y0 Z1 Y2] +\n",
      "(0.04532220209856541+0j) [Y0 Z1 Y2 Z3] +\n",
      "(0.17119774853325848+0j) [Z0] +\n",
      "(0.1711977485332586+0j) [Z0 Z1] +\n",
      "(0.16586702396410954+0j) [Z0 Z1 Z2] +\n",
      "(0.16586702396410954+0j) [Z0 Z1 Z2 Z3] +\n",
      "(0.12054482186554413+0j) [Z0 Z2] +\n",
      "(0.12054482186554413+0j) [Z0 Z2 Z3] +\n",
      "(0.16862219143347554+0j) [Z1] +\n",
      "(-0.22278592890107013+0j) [Z1 Z2 Z3] +\n",
      "(0.17434844170557132+0j) [Z1 Z3] +\n",
      "(-0.22278592890107018+0j) [Z2]\n",
      "\n",
      "the Bravyi-Kitaev-Super-Fast form is:\n",
      "(-0.09886397351781614+0j) [] +\n",
      "(0.045322202098565426+0j) [X0 X3] +\n",
      "(0.045322202098565426+0j) [Y0 Z1 Z2 Y3] +\n",
      "(0.045322202098565426+0j) [Y0 Y3] +\n",
      "(-0.0453222020985654+0j) [Z0 X1 X2 Z3] +\n",
      "(-0.0453222020985654+0j) [Z0 Y1 Y2 Z3] +\n",
      "(0.17119774853325856+0j) [Z0 Z1] +\n",
      "(0.24108964373108827+0j) [Z0 Z1 Z2 Z3] +\n",
      "(0.17119774853325856+0j) [Z0 Z2] +\n",
      "(0.3317340479282191+0j) [Z0 Z3] +\n",
      "(-0.0453222020985654+0j) [Y1 Y2] +\n",
      "(0.34297063313904685+0j) [Z1 Z2] +\n",
      "(-0.22278592890107018+0j) [Z1 Z3] +\n",
      "(-0.22278592890107016+0j) [Z2 Z3]\n",
      "\n"
     ]
    }
   ],
   "source": [
    "import openfermion\n",
    "from openfermion.hamiltonians import MolecularData\n",
    "from openfermion.ops import FermionOperator\n",
    "from openfermion.ops import InteractionOperator\n",
    "from openfermion.transforms import get_fermion_operator, get_interaction_operator, jordan_wigner, bravyi_kitaev, bravyi_kitaev_fast\n",
    "\n",
    "# set the molecular structure\n",
    "diatomic_bond_length = .7414\n",
    "geometry = [('H', (0., 0., 0.)), \n",
    "            ('H', (0., 0., diatomic_bond_length))]\n",
    "basis = 'sto-3g'  # take the basis as sto-3g, a popular Guassian basis\n",
    "multiplicity = 1\n",
    "charge = 0\n",
    "description = format(diatomic_bond_length)\n",
    "molecule = openfermion.MolecularData(geometry, basis, multiplicity, description=description)\n",
    "molecule.load()\n",
    "\n",
    "# get the total hamiltonian\n",
    "hamiltonian = molecule.get_molecular_hamiltonian()\n",
    "# print(\"the hamiltonian in the second-quantization form is:\")\n",
    "# print(hamiltonian)\n",
    "    \n",
    "# get the JW, BK, BKSF forms\n",
    "# Note: you must run the previous block code before\n",
    "get_transformation(hamiltonian)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Example 2: Lithium hydride (LiH)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "scrolled": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "the Jordan-Wigner form is:\n",
      "-7.498946902010707 [] +\n",
      "-0.0029329964409502253 [X0 X1 Y2 Y3] +\n",
      "0.0029329964409502253 [X0 Y1 Y2 X3] +\n",
      "0.01291078027311749 [X0 Z1 X2] +\n",
      "-0.0013743761078958685 [X0 Z1 X2 Z3] +\n",
      "0.011536413200774974 [X0 X2] +\n",
      "0.0029329964409502253 [Y0 X1 X2 Y3] +\n",
      "-0.0029329964409502253 [Y0 Y1 X2 X3] +\n",
      "0.01291078027311749 [Y0 Z1 Y2] +\n",
      "-0.0013743761078958685 [Y0 Z1 Y2 Z3] +\n",
      "0.011536413200774974 [Y0 Y2] +\n",
      "0.1619947538800418 [Z0] +\n",
      "0.011536413200774974 [Z0 X1 Z2 X3] +\n",
      "0.011536413200774974 [Z0 Y1 Z2 Y3] +\n",
      "0.12444770133137588 [Z0 Z1] +\n",
      "0.054130445793298836 [Z0 Z2] +\n",
      "0.05706344223424907 [Z0 Z3] +\n",
      "0.01291078027311749 [X1 Z2 X3] +\n",
      "-0.0013743761078958685 [X1 X3] +\n",
      "0.01291078027311749 [Y1 Z2 Y3] +\n",
      "-0.0013743761078958685 [Y1 Y3] +\n",
      "0.1619947538800418 [Z1] +\n",
      "0.05706344223424907 [Z1 Z2] +\n",
      "0.054130445793298836 [Z1 Z3] +\n",
      "-0.013243698330265952 [Z2] +\n",
      "0.08479609543670981 [Z2 Z3] +\n",
      "-0.013243698330265966 [Z3]\n",
      "\n",
      "the Bravyi-Kitaev form is:\n",
      "(-7.49894690201071+0j) [] +\n",
      "(0.011536413200774975+0j) [X0 X1 X2] +\n",
      "(0.0013743761078958677+0j) [X0 X1 X2 Z3] +\n",
      "(0.01291078027311749+0j) [X0 Y1 Y2] +\n",
      "(0.0029329964409502266+0j) [X0 Z1 X2] +\n",
      "(0.0029329964409502266+0j) [X0 Z1 X2 Z3] +\n",
      "(0.011536413200774975+0j) [Y0 X1 Y2] +\n",
      "(0.0013743761078958677+0j) [Y0 X1 Y2 Z3] +\n",
      "(-0.01291078027311749+0j) [Y0 Y1 X2] +\n",
      "(0.0029329964409502266+0j) [Y0 Z1 Y2] +\n",
      "(0.0029329964409502266+0j) [Y0 Z1 Y2 Z3] +\n",
      "(0.16199475388004184+0j) [Z0] +\n",
      "(0.011536413200774975+0j) [Z0 X1 Z2] +\n",
      "(0.0013743761078958677+0j) [Z0 X1 Z2 Z3] +\n",
      "(-0.012910780273117487+0j) [Z0 X1 Z3] +\n",
      "(0.16199475388004186+0j) [Z0 Z1] +\n",
      "(0.05706344223424907+0j) [Z0 Z1 Z2] +\n",
      "(0.05706344223424907+0j) [Z0 Z1 Z2 Z3] +\n",
      "(0.054130445793298836+0j) [Z0 Z2] +\n",
      "(0.054130445793298836+0j) [Z0 Z2 Z3] +\n",
      "(-0.0013743761078958677+0j) [X1] +\n",
      "(0.012910780273117487+0j) [X1 Z2] +\n",
      "(-0.011536413200774975+0j) [X1 Z3] +\n",
      "(0.12444770133137588+0j) [Z1] +\n",
      "(-0.013243698330265952+0j) [Z1 Z2 Z3] +\n",
      "(0.08479609543670981+0j) [Z1 Z3] +\n",
      "(-0.013243698330265966+0j) [Z2]\n",
      "\n",
      "the Bravyi-Kitaev-Super-Fast form is:\n",
      "(-7.498946902010707+0j) [] +\n",
      "(0.002932996440950222+0j) [X0 X5] +\n",
      "(0.002932996440950222+0j) [Y0 Z1 Z4 Y5] +\n",
      "(0.002932996440950222+0j) [Y0 Z2 Z3 Y5] +\n",
      "(0.012910789308670843+0j) [Z0 Y1 Z2 Z3 Z4] +\n",
      "(-0.012910780273117513+0j) [Z0 Y1 Z3 Z5] +\n",
      "(-0.0029329964409502284+0j) [Z0 Z1 X2 X3 Z4 Z5] +\n",
      "(0.1619947538800418+0j) [Z0 Z1 Z2] +\n",
      "(0.012910789308670843+0j) [Z0 Z1 Y4] +\n",
      "(0.11412688446849814+0j) [Z0 Z1 Z4 Z5] +\n",
      "(-0.0029329964409502284+0j) [Z0 Y2 Y3 Z5] +\n",
      "(0.10826089158659767+0j) [Z0 Z2 Z3 Z5] +\n",
      "(-0.012910780273117513+0j) [Z0 Z3 Y4 Z5] +\n",
      "(0.1619947538800418+0j) [Z0 Z3 Z4] +\n",
      "(0.012910780273117513+0j) [Y1 Z2] +\n",
      "(-0.012910789308670843+0j) [Y1 Z4 Z5] +\n",
      "(-0.0029329964409502284+0j) [Z1 Y2 Y3 Z4] +\n",
      "(-0.012910789308670843+0j) [Z1 Z2 Z3 Y4 Z5] +\n",
      "(0.2092437967680857+0j) [Z1 Z2 Z3 Z4] +\n",
      "(-0.013243698330265938+0j) [Z1 Z3 Z5] +\n",
      "(0.012910780273117513+0j) [Z2 Y4] +\n",
      "(-0.013243698330265952+0j) [Z2 Z4 Z5]\n",
      "\n"
     ]
    }
   ],
   "source": [
    "import openfermion\n",
    "from openfermion.hamiltonians import MolecularData\n",
    "from openfermion.ops import FermionOperator\n",
    "from openfermion.ops import InteractionOperator\n",
    "from openfermion.transforms import get_fermion_operator, get_interaction_operator, jordan_wigner, bravyi_kitaev, bravyi_kitaev_fast\n",
    "# set the molecular structure\n",
    "diatomic_bond_length = 1.45\n",
    "geometry = [('Li', (0., 0., 0.)), ('H', (0., 0., diatomic_bond_length))]\n",
    "basis = 'sto-3g'\n",
    "multiplicity = 1\n",
    "\n",
    "# Set Hamiltonian parameters. This will limit the number of valence electron and electronic orbitals considered.\n",
    "# You can play with it to obtain different total hamiltonians and their corresponding JW/BK forms\n",
    "active_space_start = 1\n",
    "active_space_stop = 3\n",
    "\n",
    "# Get the Hamiltonian.\n",
    "molecule = MolecularData(geometry, basis, multiplicity, description=\"1.45\")\n",
    "molecule.load()\n",
    "hamiltonian = molecule.get_molecular_hamiltonian(\n",
    "    occupied_indices=range(active_space_start),\n",
    "    active_indices=range(active_space_start, active_space_stop))\n",
    "\n",
    "# get the JW, BK, BKSF forms\n",
    "# Note: you must run the previous block code before\n",
    "get_transformation(hamiltonian)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Example 3: 1D Free Electron Gas (Jellium)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "scrolled": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "19.50047638754088 [] +\n",
      "-9.71044945799746 [Z0] +\n",
      "-0.07957747154594767 [Z0 Z1] +\n",
      "-0.07957747154594767 [Z0 Z2] +\n",
      "0.15915494309189535 [Z1] +\n",
      "-0.07957747154594767 [Z1 Z2] +\n",
      "-9.71044945799746 [Z2]\n",
      "\n",
      "19.500476387540857 [] +\n",
      "-3.289868133696451 [X0 X1] +\n",
      "-3.2898681336964564 [X0 Z1 X2] +\n",
      "-3.289868133696451 [Y0 Y1] +\n",
      "-3.2898681336964564 [Y0 Z1 Y2] +\n",
      "-6.42058132430101 [Z0] +\n",
      "-0.07957747154594766 [Z0 Z1] +\n",
      "-0.07957747154594767 [Z0 Z2] +\n",
      "-3.289868133696451 [X1 X2] +\n",
      "-3.289868133696451 [Y1 Y2] +\n",
      "-6.4205813243010095 [Z1] +\n",
      "-0.07957747154594766 [Z1 Z2] +\n",
      "-6.42058132430101 [Z2]\n",
      "\n",
      "[2.92682545e-14 2.83939539e-14 2.84217094e-14 1.42108547e-14\n",
      " 2.48689958e-14 1.77635684e-14 7.10542736e-15 1.42108547e-14]\n"
     ]
    }
   ],
   "source": [
    "from openfermion.hamiltonians import jellium_model\n",
    "from openfermion.utils import eigenspectrum, fourier_transform, Grid\n",
    "from openfermion.transforms import jordan_wigner\n",
    "\n",
    "# Let's look at a very small model of jellium in 1D.\n",
    "grid = Grid(dimensions=1, length=3, scale=1.0)\n",
    "spinless = True\n",
    "\n",
    "# Get the momentum Hamiltonian.\n",
    "momentum_hamiltonian = jellium_model(grid, spinless)\n",
    "momentum_qubit_operator = jordan_wigner(momentum_hamiltonian)\n",
    "momentum_qubit_operator.compress()\n",
    "print(momentum_qubit_operator)\n",
    "\n",
    "# Fourier transform the Hamiltonian to the position basis.\n",
    "position_hamiltonian = fourier_transform(momentum_hamiltonian, grid, spinless)\n",
    "position_qubit_operator = jordan_wigner(position_hamiltonian)\n",
    "position_qubit_operator.compress()\n",
    "print('')\n",
    "print (position_qubit_operator)\n",
    "\n",
    "# Check the spectra to make sure these representations are iso-spectral.\n",
    "spectral_difference = eigenspectrum(momentum_qubit_operator) -  eigenspectrum(position_qubit_operator)\n",
    "print('')\n",
    "print(spectral_difference)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Appendix: Define Specific Fermionic Operator for JW and BK Testing"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "3.17 [0^ 2] +\n",
      "3.17 [2^ 0]\n",
      "\n",
      "(1.585+0j) [X0 Z1 X2] +\n",
      "(1.585+0j) [Y0 Z1 Y2]\n",
      "\n",
      "(1.585+0j) [X0 Y1 Y2] +\n",
      "(-1.585+0j) [Y0 Y1 X2]\n"
     ]
    }
   ],
   "source": [
    "from openfermion.ops import FermionOperator\n",
    "from openfermion.transforms import jordan_wigner, bravyi_kitaev\n",
    "from openfermion.utils import eigenspectrum, hermitian_conjugated\n",
    "\n",
    "# Initialize an operator.\n",
    "fermion_operator = FermionOperator('2^ 0', 3.17)\n",
    "fermion_operator += hermitian_conjugated(fermion_operator)\n",
    "print(fermion_operator)\n",
    "\n",
    "# Transform to qubits under the Jordan-Wigner transformation and print its spectrum.\n",
    "jw_operator = jordan_wigner(fermion_operator)\n",
    "print()\n",
    "print(jw_operator)\n",
    "\n",
    "# Transform to qubits under the Bravyi-Kitaev transformation and print its spectrum.\n",
    "bk_operator = bravyi_kitaev(fermion_operator)\n",
    "print()\n",
    "print(bk_operator)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
